﻿using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace Quail
{
    /// <summary>
    /// Implements access to SELECT html control
    /// </summary>
    public class WebList : WebControl, IWebControlWithItems
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="WebList"/> class.
        /// </summary>
        /// <param name="container">The control's container (a page or anothe webcontrol)</param>
        /// <param name="selector">The jQuery selector for the control</param>
        public WebList(WebControlContainer container, string selector)
            : base(container, selector)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebList"/> class.
        /// </summary>
        /// <param name="control">The control to make a copy</param>
        public WebList(WebControl control)
            : base(control)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebList"/> class.
        /// </summary>
        public WebList()
        {
        }

        /// <summary>
        /// Gets or sets the selected value of the SELECT
        /// </summary>
        public string Value
        {
            get
            {
                var task = BeginTask("Get Value");
                try
                {
                    var result = Browser.SeleniumInterface.GetSelectedValue(Locator);
                    task.Finish(result);
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }

            set
            {
                var task = BeginTask("Set Value");
                task.AddAttribute("Value", value);
                try
                {
                    Browser.SeleniumInterface.Select(Locator, "value=" + value);
                    task.Finish();
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected label of the SELECT (the label is what is displayed to the user)
        /// </summary>
        public string Label
        {
            get
            {
                var task = BeginTask("Get Label");
                try
                {
                    var result = Browser.SeleniumInterface.GetSelectedLabel(Locator);
                    task.Finish(result);
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }

            set
            {
                var task = BeginTask("Set Label");
                task.AddAttribute("Label", value);
                try
                {
                    Browser.SeleniumInterface.Select(Locator, "label=" + value);
                    task.Finish();
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets or sets the index of the selected item in the list (0-based)
        /// </summary>
        public int SelectedIndex
        {
            get
            {
                var task = BeginTask("Get SelectedIndex");
                try
                {
                    var result = int.Parse(Browser.SeleniumInterface.GetSelectedIndex(Locator));
                    task.Finish(result.ToString());
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }

            set
            {
                var task = BeginTask("Set SelectedIndex");
                task.AddAttribute("SelectedIndex", value.ToString());
                try
                {
                    Browser.SeleniumInterface.Select(Locator, "index=" + value);
                    task.Finish();
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the list of labels (visible text) for the SELECT control
        /// </summary>
        public string[] Labels
        {
            get
            {
                var task = BeginTask("Get Labels");
                try
                {
                    var result = Browser.SeleniumInterface.GetSelectOptions(Locator);
                    string log = string.Empty;
                    foreach (var r in result)
                    {
                        if (log.Length > 0)
                            log += ", ";
                        log += r;
                    }

                    task.Finish(log);
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the list of values for the SELECT control
        /// </summary>
        public string[] Values
        {
            get
            {
                var task = BeginTask("Get Values");
                try
                {
                    var result = Browser.DoCommandToStringArray("getSelectValues", Locator);
                    string log = string.Empty;
                    foreach (var r in result)
                    {
                        if (log.Length > 0)
                            log += ", ";
                        log += r;
                    }

                    task.Finish(log);
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the number of items in the list
        /// </summary>
        public int ItemCount
        {
            get
            {
                var task = BeginTask("Get ItemCount");
                try
                {
                    var result = int.Parse(Browser.DoCommand("getSelectItemCount", Locator));
                    task.Finish(result.ToString());
                    return result;
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the label for a specific item index
        /// </summary>
        /// <param name="index">the index to get</param>
        /// <returns>the label value</returns>
        public string GetLabel(int index)
        {
            return Labels[index];
        }

        /// <summary>
        /// Gets the value for a specific item index
        /// </summary>
        /// <param name="index">the index to get</param>
        /// <returns>the value of the item</returns>
        public string GetValue(int index)
        {
            return Values[index];
        }


        /// <summary>
        /// Checks to see if a value is represented in the list
        /// </summary>
        /// <param name="value">The value to search for</param>
        /// <returns>True if the value is in the group</returns>
        public bool ValueExists(string value)
        {
            return Values.Any(x => x == value);
        }

        /// <summary>
        /// Checks to see if a label is represented in the list
        /// </summary>
        /// <param name="label">The label to search for</param>
        /// <returns>True if the label is in the group</returns>
        public bool LabelExists(string label)
        {
            return Labels.Any(x => x == label);
        }

        /// <summary>
        /// Selects a random item from the list
        /// </summary>
        /// <param name="selectDifferentValue">If the value should be different than the currently selected value</param>
        public void SelectRandomValue(bool selectDifferentValue)
        {
            if (selectDifferentValue)
            {
                string currentlySelected = Value;

                do
                {
                    SelectRandomValue();
                }
                while (Value == currentlySelected);
            }
            else
            {
                SelectRandomValue();
            }
        }

        /// <summary>
        /// Selects a random item from the list
        /// </summary>
        public void SelectRandomValue()
        {
            var task = ActiveTask.BeginTask("SelectRandomValue");
            try
            {
                var count = ItemCount;

                if (count == 0)
                    throw new WebControlException(this, "The list [" + Selector + "] doesn't have any items to select!");

                var idx = new Random().Next(0, count);

                SelectedIndex = idx;

                task.AddAttribute("Value", Value);
                
                task.Finish();
            }
            catch (Exception e)
            {
                task.Finish(e);
                throw;
            }
        }

        /// <summary>
        /// Selects an item based on a regex
        /// </summary>
        /// <param name="item">the item to select</param>
        /// <returns>the value that was selected</returns>
        public string SelectValue(Regex item)
        {
            foreach (var value in Values)
            {
                if (item.IsMatch(value))
                {
                    Value = value;
                    return value;
                }
            }

            throw new WebControlException(this, "SelectValue unable to find value matching regex: " + item);
        }

        /// <summary>
        /// Selects a label based on a regex
        /// </summary>
        /// <param name="item">the item to select</param>
        /// <returns>the label that was selected</returns>
        public string SelectLabel(Regex item)
        {
            foreach (var label in Labels)
            {
                if (item.IsMatch(label))
                {
                    Label = label;
                    return label;
                }
            }

            throw new WebControlException(this, "SelectValue unable to find label matching regex: " + item);
        }

        /// <summary>
        /// Clears the selected item
        /// </summary>
        public override void Clear()
		{
            var task = BeginTask("Clear");
			try
			{
                SelectedIndex = 0;
			    task.Finish();
			}
            catch (Exception e)
            {
                task.Finish(e);
                throw;
            }
		}
    }
}
